package main

import (
	"net"
	"os"
	"strconv"
	"time"
	"wind/wind-config"
	"github.com/name5566/leaf/log"
)

type Request struct {
	isCancel bool
	reqSeq   int
	reqPkg   []byte
	rspChan  chan <- []byte
}

func main() {
	wind_config.ConfigServer("wind-config/agent.json")

	addr, err := net.ResolveUDPAddr("udp", wind_config.ServerConfig.ServerHost + ":" + strconv.Itoa(wind_config.ServerConfig.ServerPort))
	if err != nil {
		log.Fatal("net.ResolveUDPAddr fail.", err)
		os.Exit(1)
	}

	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		log.Fatal("net.ListenUDP fail.", err)
		os.Exit(1)
	}
	defer conn.Close()

	reqChan := make(chan *Request, wind_config.ServerConfig.BuffSize)
	go connHandler(reqChan)

	var seq int = 0
	for {
		buf := make([]byte, wind_config.ServerConfig.BuffSize)
		rlen, client, err := conn.ReadFromUDP(buf)
		if err != nil {
			log.Error("conn.ReadFromUDP fail.", err)
			continue
		}
		seq++
		go processHandler(conn, client, buf[:rlen], reqChan, seq)
	}
}

func processHandler(conn *net.UDPConn, client *net.UDPAddr, msg []byte, reqChan chan <- *Request, seq int) {
	rspChan := make(chan []byte, 1)
	reqChan <- &Request{false, seq, []byte(strconv.Itoa(seq)), rspChan}
	select {
	case rsp := <-rspChan:
		log.Debug("recv rsp ", string(rsp))
		conn.WriteToUDP(rsp, client)
	case <-time.After(time.Duration(wind_config.ServerConfig.MaxTimeOut) * time.Second):
		log.Debug("wait for rsp timeout.")
		reqChan <- &Request{isCancel: true, reqSeq: seq}
		conn.WriteToUDP([]byte("wait for rsp timeout."), client)
		return
	}

	conn.WriteToUDP([]byte("write all msg to server"), client)
}

func connHandler(reqChan <-chan *Request) {
	service_addr, err := net.ResolveUDPAddr("udp", wind_config.ServerConfig.Host + ":" + strconv.Itoa(wind_config.ServerConfig.Port))
	if err != nil {
		log.Error("net.ResolveUDPAddr fail.", err)
		os.Exit(1)
	}

	conn, err := net.DialUDP("udp", nil, service_addr)
	if err != nil {
		log.Error("net.DialUDP fail.", err)
		os.Exit(1)
	}
	defer conn.Close()

	sendChan := make(chan []byte, wind_config.ServerConfig.BuffSize)
	go sendHandler(conn, sendChan)

	recvChan := make(chan []byte, wind_config.ServerConfig.BuffSize)
	go recvHandler(conn, recvChan)

	reqMap := make(map[int]*Request)
	for {
		select {
		case req := <-reqChan:
			if req.isCancel {
				delete(reqMap, req.reqSeq)
				log.Debug("CancelRequest recv. reqSeq ", req.reqSeq)
				continue
			}
			reqMap[req.reqSeq] = req
			sendChan <- req.reqPkg
			log.Debug("NormalRequest recv. reqSeq=%s reqPkg=%s", req.reqSeq, string(req.reqPkg))
		case rsp := <-recvChan:
			seq, err := strconv.Atoi(string(rsp))
			if err != nil {
				log.Debug("strconv.Atoi fail. err ", err)
				continue
			}
			req, ok := reqMap[seq]
			if !ok {
				log.Debug("seq not found. seq ", seq)
				continue
			}
			req.rspChan <- rsp
			log.Debug("send rsp to client. rsp ", string(rsp))
			delete(reqMap, req.reqSeq)
		}
	}
}

func sendHandler(conn *net.UDPConn, sendChan <-chan []byte) {
	for data := range sendChan {
		wlen, err := conn.Write(data)
		if err != nil || wlen != len(data) {
			log.Error("conn.Write fail.", err)
			continue
		}
		log.Debug("conn.Write succ. data ", string(data))
	}
}

func recvHandler(conn *net.UDPConn, recvChan chan <- []byte) {
	for {
		buf := make([]byte, wind_config.ServerConfig.BuffSize)
		rlen, err := conn.Read(buf)
		if err != nil || rlen <= 0 {
			log.Error("conn.Read fail.", err)
			continue
		}
		log.Debug("conn.Read succ. data ", string(buf))
		recvChan <- buf[:rlen]
	}
}
